package com.netty.server.cmd;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.Unpooled;
import lombok.Builder;
import lombok.Data;
import lombok.Getter;

import java.io.Serializable;
import java.util.stream.Stream;

/**
 * 设备与服务器之间协议传输的基本响应响应指令对象
 *
 * @author: 王霄
 * @date: 2022/1/17 14:07
 **/
@Data
@Builder
public class ByteResCmd implements Serializable {

    private byte cmdType;

    private long cmdId;

    private Code resCode;

    private byte[] body;

    public static ByteResCmd deserialize(byte[] bs) {
        if (bs == null || bs.length == 0) {
            throw new RuntimeException("空数据包!");
        }
        ByteBuf buf = Unpooled.wrappedBuffer(bs);
        try {
            buf.skipBytes(1);
            //1B 协议类型
            byte cmdType = buf.readByte();
            //4B 协议ID
            long cmdId = buf.readUnsignedInt();
            //1B 响应码
            byte resCode = buf.readByte();
            //1B 响应码
            int bodyLen = buf.readUnsignedShort();
            //NB 协议体
            byte[] body = new byte[bodyLen];
            buf.readBytes(body);
            return ByteResCmd.builder()
                    .cmdType(cmdType)
                    .cmdId(cmdId)
                    .resCode(Code.get(resCode))
                    .body(body)
                    .build();
        } finally {
            buf.release();
        }
    }

    /**
     * 根据原始CMD构造一个简单响应
     */
    public static ByteResCmd build(ByteCmd origin, Code code, byte[] body) {
        return ByteResCmd.builder()
                .cmdType(origin.getCmdType())
                .cmdId(origin.getCmdId())
                .resCode(code)
                .body(body)
                .build();
    }

    public static ByteResCmd build(ByteCmd origin, Code code) {
        return build(origin, code, null);
    }

    public byte[] serializer() {
        ByteBuf buf = ByteBufAllocator.DEFAULT.heapBuffer();
        try {
            buf.writeByte(0XFF);
            buf.writeByte(cmdType);
            buf.writeInt(Math.toIntExact(cmdId));
            buf.writeByte(resCode.getCode());
            if (body != null) {
                buf.writeShort(body.length);
                buf.writeBytes(body);
            } else {
                buf.writeShort(0);
            }
            byte[] bs = new byte[buf.readableBytes()];
            buf.readBytes(bs);
            return bs;
        } catch (Exception e) {
            throw new RuntimeException("解析异常", e);
        } finally {
            buf.release();
        }
    }

    /**
     * 响应指令类型
     */
    public enum Code {
        OK((byte) 0X00),
        FAIL((byte) 0X01);
        @Getter
        private final byte code;

        Code(byte code) {
            this.code = code;
        }

        public static Code get(byte b) {
            return Stream.of(Code.values())
                    .filter(t -> t.code == b)
                    .findFirst()
                    .orElseThrow(() -> new RuntimeException("未知的响应码类型"));
        }
    }
}